/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package czStvDpds.graphics3D.geometry;

import czStvDpds.graphics3D.TextureUnitSettings;
import java.nio.ByteBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL14;
import org.lwjgl.util.vector.Matrix4f;

/**
 * Reprezntuje texturu.
 *
 * @author divad
 */
public class Texture {

    private ByteBuffer data;
    private int textureID = 0, textureCoordsIndex = 0;
    private TextureProperties properties;
    private TextureEnvironment environment;
    private Matrix4f matrix;
    private boolean created = false;

    /**
     * Vytvoří novou texturu. </b>NE v GL! Pouze jako javovskou instanci</b>
     */
    public Texture() {
        matrix = new Matrix4f();
        this.environment = new TextureEnvironment();
    }

    /**
     * Použije texturu a aktivuje příslušnou texturovací jednotku.
     *
     * @param textureUnitIndex - index použité texturovací jednotky (0 - 31)
     */
    void use(TextureUnitSettings tus) {
        if (tus.getTextureUnitNumber() >= 0 && this.isCreated()) {
            tus.adjustGLToTexture(this);
            GL11.glBindTexture(this.properties.getType(), textureID);
        }

    }

    /**
     * Vrátí nastavení textury.
     *
     * @return
     */
    public TextureProperties getProperties() {
        return properties;
    }

    /**
     * Nastaví texturu.
     *
     * @param properties
     */
    public void setProperties(TextureProperties properties) {
        this.properties = properties;
    }

    /**
     * Vrátí id textury, je li vytvořená v GL
     *
     * @return
     */
    public int getTextureID() {
        return textureID;
    }

    /**
     * Vrátí data obrázku/obrázků
     *
     * @return
     */
    public ByteBuffer getData() {
        return data;
    }

    /**
     * Vrátí index texturových koordinátů vůči geometrii objektu. Pokud se
     * texturové koordináty nepoužívají, vrací -1.
     *
     * @return
     */
    public int getTextureCoordsIndex() {
        return textureCoordsIndex;
    }

    /**
     * Nastaví index texturových koordinátů vůči geometrii objektu. Nastav -1
     * pokud se texturové koordináty nepoužívájí.
     *
     * @return
     */
    public void setTextureCoordsIndex(int textureCoordsIndex) {
        this.textureCoordsIndex = textureCoordsIndex;
    }

    /**
     * Nastaví data obrázku/obrázků
     *
     * @param data
     */
    public void setData(ByteBuffer data) {
        this.data = data;
    }

    public void setMatrix(Matrix4f matrix) {
        this.matrix = matrix;
    }

    public void setEnvironment(TextureEnvironment environment) {
        this.environment = environment;
    }

    public Matrix4f getMatrix() {
        return matrix;
    }

    public TextureEnvironment getEnvironment() {
        return environment;
    }

    /**
     * Nastaví texturu.
     *
     * @param data
     * @param properties
     * @param id
     */
    public void set(ByteBuffer data, TextureProperties properties, int id) {
        this.data = data;
        this.properties = properties;
        this.textureID = id;
        this.created = id > 0;
    }

    /**
     * Nastaví texturu v GL. Mělo by být voláno jen hlavní vykrelovací
     * jednotkou.
     */
    public void setInGL() {
        GL11.glBindTexture(properties.getType(), this.textureID);
        GL11.glTexParameteri(properties.getType(), GL11.GL_TEXTURE_MIN_FILTER, properties.getFilterMin());
        GL11.glTexParameteri(properties.getType(), GL11.GL_TEXTURE_MAG_FILTER, properties.getFilterMax());
        GL11.glTexParameteri(properties.getType(), GL11.GL_TEXTURE_WRAP_S, properties.getWrapS());
        GL11.glTexParameteri(properties.getType(), GL11.GL_TEXTURE_WRAP_T, properties.getWrapT());
        GL11.glTexParameteri(properties.getType(), GL12.GL_TEXTURE_WRAP_R, properties.getWrapR());
        GL11.glTexParameteri(properties.getType(), GL14.GL_TEXTURE_COMPARE_MODE, properties.isCompare() ? GL14.GL_COMPARE_R_TO_TEXTURE : GL11.GL_NONE);
        GL11.glTexParameteri(properties.getType(), GL14.GL_TEXTURE_COMPARE_FUNC, properties.getCompareFunction());
        GL11.glBindTexture(properties.getType(), 0);
    }

    /**
     * Vrátí zda je textura vytvořená v GL
     *
     * @return
     */
    public boolean isCreated() {
        return this.created;
    }

    /**
     * Vyrvoří naprosto nezávislou kopii této textury. Nová textura nebude
     * vytvořena v GL.
     *
     * @return
     */
    public Texture copyTextureValue() {
        Texture t = new Texture();
        ByteBuffer data = BufferUtils.createByteBuffer(this.data.capacity());
        data.put(this.data);
        data.rewind();
        t.setData(data);
        TextureProperties tp = new TextureProperties(this.properties);
        t.setProperties(tp);
        TextureEnvironment te = new TextureEnvironment(this.environment);
        t.setEnvironment(te);
        t.setMatrix(new Matrix4f(this.matrix));
        t.setTextureCoordsIndex(textureCoordsIndex);
        return t;
    }

    /**
     * Vytvoří Texturu která bude sdílet stejná data, nastavení a GLTextureIndex
     * s původní texturou. Tzn. když se něco změní v původní textuře, změní se
     * to i v této nově vytvořené textuře. Tyto vazby moho být zrušeny novým
     * nastavením textury. Pokud se originální textura vytvoří v GL až po
     * získání její kopie, tato kopie nebude v GL vyvořena.
     *
     * @return
     */
    public Texture copyTextureReference() {
        Texture t = new Texture();
        t.set(data, properties, textureID);
        t.setEnvironment(environment);
        t.setMatrix(matrix);
        t.setTextureCoordsIndex(textureCoordsIndex);
        return t;
    }
}
